package com.calmcar.adas;

import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.os.MemoryFile;
import android.util.Log;

import com.calmcar.adas.apiserver.model.BufferBean;
import com.calmcar.adas.apiserver.model.JavaCameraFrame;
import com.calmcar.adas.apiserver.out.CvCameraViewFrame;

import org.opencv.core.CvType;
import org.opencv.core.Mat;

import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * Created by lelexiao on 2018/5/23.
 */

public class CameraDataProcess {
    private static final String TAG = "CameraDataProcess";
    private MemoryFile mMemoryFile;
    BufferBean mBufferBean;
    private Thread mThread;
    private boolean mStopThread;
    protected Camera mCamera;

    private Mat mFrameChain;
    protected JavaCameraFrame mCameraFrame;
    protected int mFrameWidth;
    protected int mFrameHeight;

    public int readMemoryByte() {
        try {
            if (mMemoryFile != null) {
                mMemoryFile.readBytes(mBufferBean.isCanRead, 0, 0, 1);
                if (mBufferBean.isCanRead[0] == 1) {    //可读
                    mMemoryFile.readBytes(mBufferBean.mBuffer, 1, 0, mBufferBean.mBuffer.length);
                    if (mBufferBean.mBuffer != null) {
                        mFrameChain.put(0, 0, mBufferBean.mBuffer);
                        mListener.onCameraFrame(mCameraFrame);
                    }
                    mBufferBean.isCanRead[0] = 0;   //可写
                    mMemoryFile.writeBytes(mBufferBean.isCanRead, 0, 0, 1);
                }
            }
        } catch (Exception e) {
            return -1;
        }
        return 0;
    }


    public void createShareMemory() {
         //  创建共享内存区-start
        int BUFFER_SIZE = mFrameWidth * mFrameHeight * 3 / 2;     //FIXME: if preview size is bigger than 960x540, need modify this value
        int MEMORY_SIZE = BUFFER_SIZE + 1;
        mBufferBean = new BufferBean(BUFFER_SIZE);
        try {
            mMemoryFile = new MemoryFile(TAG, MEMORY_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mFrameChain = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1);
        mCameraFrame= new JavaCameraFrame(mFrameChain, mFrameWidth, mFrameHeight, ImageFormat.YV12);
        setMemoryShareFD(mCamera);
        // /创建共享区-end
    }


    public int setMemoryShareFD(Camera camera) {
        int err = -1;
        try {
            if (camera != null) {
                Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
                FileDescriptor fd = (FileDescriptor) method.invoke(mMemoryFile);
                setMemoryFileFileDescriptor(camera, fd);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return err;
    }


    private class CameraWorker implements Runnable {
        @Override
        public void run() {
            do {
                readMemoryByte();
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (!mStopThread);
            Log.d(TAG, "Finish processing thread");
        }
    }


    public void startReadThread() {
        mStopThread = false;
        mThread = new Thread(new CameraWorker());
        mThread.start();
    }


    public  void stopReadThread(){
        try {
            mStopThread = true;
            Log.d(TAG, "Notify thread");
            synchronized (this) {
                this.notify();
            }
            Log.d(TAG, "Wating for thread");
            if (mThread != null)
                mThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mThread =  null;
        }

    }


    private int setMemoryFileFileDescriptor(Camera camera, FileDescriptor fileDescriptor) {
        try {
            Class<?> classType = Class.forName("android.hardware.Camera");
            Method meth = classType.getMethod("setMemoryFileFileDescriptor", new Class[]{FileDescriptor.class});
            if (meth != null) {
                Object retobj = meth.invoke(camera, new Object[]{fileDescriptor});
                return (Integer) retobj;
            } else {
            }
        } catch (Exception e) {
        }
        return -1;
    }


    public CameraDataProcess(int mFrameWidth, int mFrameHeight, CameraDataArriveListener mListener) {
        this.mFrameWidth = mFrameWidth;
        this.mFrameHeight = mFrameHeight;
        this.mListener = mListener;
        initializeCamera();
    }

    public CameraDataProcess(int mFrameWidth, int mFrameHeight) {
        this.mFrameWidth = mFrameWidth;
        this.mFrameHeight = mFrameHeight;
        initializeCamera();
    }

    protected boolean initializeCamera() {
        Log.d(TAG, "Initialize java camera");
        boolean result = true;
        int localCameraIndex=-1;
        synchronized (this) {
            mCamera = null;
                Log.i(TAG, "Trying to open back camera");
                Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
                for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) {
                    Camera.getCameraInfo( camIdx, cameraInfo );
                    if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                        localCameraIndex = camIdx;
                        break;
                    }
                }
        }
        if(localCameraIndex==-1){
            Log.e(TAG, "Back camera not found!");
        }
        try {
            mCamera = Camera.open(localCameraIndex);
        } catch (RuntimeException e) {
            Log.e(TAG, "Camera #" + localCameraIndex + "failed to open: " + e.getLocalizedMessage());
        }
        if (mCamera == null)
            return false;
        createShareMemory();
        return result;
    }

    public CameraDataArriveListener getmListener() {
        return mListener;
    }

    public void setmListener(CameraDataArriveListener mListener) {
        this.mListener = mListener;
    }

    private CameraDataArriveListener  mListener;
    public interface CameraDataArriveListener {
       void  onCameraFrame(CvCameraViewFrame inputFrame);
    };

    protected void releaseCamera() {
        synchronized (this) {
            if (mCamera != null) {
                mCamera.stopPreview();
                mCamera.setPreviewCallback(null);
                mCamera.release();
            }
            mCamera = null;
            if (mFrameChain != null) {
                mFrameChain.release();
            }
            if (mCameraFrame != null) {
                mCameraFrame.release();
            }
        }
    }

}
